using System;
using System.Linq;
using System.Net;
using Moq;
using NUnit.Framework;
using OpenWaves.ActivityFeed.Web;
using OpenWaves.ActivityFeed.Web.Handler;
using OpenWaves.ActivityFeed.Web.Modules;

namespace OpenWaves.ActivityFeed.UnitTests.Web
{
    [TestFixture]
    public class CommentsModuleTests
    {
        [TestCase("POST", "/activityFeed/updates/123/comments")]
        [TestCase("DELETE", "/activityFeed/updates/123/comments/456")]
        public void When_user_is_not_authenticated_Then_routes_return_Unauthorized(string verb, string route)
        {
            var identityProvider = IdentityProviderMock.WithAnonymousUser();
            var browser = new Browser(new ActivityFeedApiHandler(identityProvider, 
                new CommentsModule(
                    new Mock<IActivityFeedService>().Object,
                    identityProvider, 
                    new Mock<IUserProfileProvider>().Object)));

            var response = browser.MakeRequest(verb, route);

            Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.Unauthorized));
        }

        [Test]
        public void Given_authenticated_user_POST_to_comments_adds_comment_by_the_user_and_returns_comment_id()
        {
            var activityFeedServiceMock = new Mock<IActivityFeedService>(MockBehavior.Strict);
            var userInfoProviderMock = new Mock<IUserProfileProvider>();

            const string updateId = "upd/1";

            var update = DummyUpdate.From("john");
            activityFeedServiceMock.Setup(service => service.GetUpdate(updateId)).Returns(update);
            userInfoProviderMock.Setup(provider => provider.GetUserProfile("sam")).Returns(new FakeUserInfo { Name = "Sam Smith", PhotoUrl = "123" });

            var userIdentityProvider = IdentityProviderMock.With("sam");
            var activityFeedApiHandler = new ActivityFeedApiHandler(userIdentityProvider, 
                new CommentsModule(activityFeedServiceMock.Object, userIdentityProvider, userInfoProviderMock.Object));

            var browser = new Browser(activityFeedApiHandler);

            var response = browser.PostJson(string.Format("/activityFeed/updates/{0}/comments", Url.Encode(updateId)), new {text = "comment text"});
            var result = response.DeserializeBody<Result<CommentDto>>();

            Assert.That(update.Comments.First().PublishedBy, Is.EqualTo("sam"));
            Assert.That(result.Value.Id, Is.Not.Empty);
        }

        [Test]
        public void Given_authenticated_user_POST_to_comments_for_non_existing_update_returns_NotFound()
        {
            const string updateId = "upd/1";

            var activityFeedServiceMock = new Mock<IActivityFeedService>(MockBehavior.Strict);
            activityFeedServiceMock.Setup(service => service.GetUpdate(updateId)).Returns(null as Update);

            var userIdentityProvider = IdentityProviderMock.With("john");
            var apiHandler = new ActivityFeedApiHandler(userIdentityProvider, new CommentsModule(activityFeedServiceMock.Object, userIdentityProvider, new Mock<IUserProfileProvider>().Object));
            var browser = new Browser(apiHandler);

            var response = browser.PostJson(
                string.Format("/activityFeed/updates/{0}/comments", Url.Encode(updateId)),new { text = "comment text"});

            Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.NotFound));
        }

        [Test]
        public void Given_authenticated_comment_publisher_DELETE_to_comments_removes_comment_from_update()
        {
            const string updateId = "update/111";
            var update = DummyUpdate.From("sam");
            var commentId = update.AddComment("comment", "john").Id;

            var activityFeedServiceMock = new Mock<IActivityFeedService>(MockBehavior.Strict);
            activityFeedServiceMock.Setup(service => service.GetUpdate(updateId)).Returns(update);

            var userIdentityProvider = IdentityProviderMock.With("john");
            var browser = new Browser(new ActivityFeedApiHandler(userIdentityProvider, 
                new CommentsModule(
                    activityFeedServiceMock.Object,
                    userIdentityProvider,
                    new Mock<IUserProfileProvider>().Object)));

            browser.Delete(string.Format("/activityFeed/updates/{0}/comments/{1}", Url.Encode(updateId), Url.Encode(commentId)));

            Assert.That(update.Comments, Is.Empty);
        }

        [Test]
        public void Given_authenticated_user_DELETE_to_comments_for_non_existing_update_returns_NotFound()
        {
            const string updateId = "update/1";

            var activityFeedServiceMock = new Mock<IActivityFeedService>(MockBehavior.Strict);
            activityFeedServiceMock.Setup(service => service.GetUpdate(updateId)).Returns(null as Update);
            var userInfoProviderMock = new Mock<IUserProfileProvider>();

            var userIdentityProvider = IdentityProviderMock.With("john");
            var browser = new Browser(new ActivityFeedApiHandler(userIdentityProvider,
                new CommentsModule(
                    activityFeedServiceMock.Object,
                    userIdentityProvider,
                    userInfoProviderMock.Object)));

            var response = browser.Delete(String.Format("/activityFeed/updates/{0}/comments/{1}", Url.Encode(updateId), Url.Encode("123")));

            Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.NotFound));
        }

        [Test]
        public void Given_authenticated_user_DELETE_to_non_existing_comment_returns_NotFound()
        {
            const string updateId = "update/123";

            var activityFeedServiceMock = new Mock<IActivityFeedService>(MockBehavior.Strict);
            activityFeedServiceMock.Setup(service => service.GetUpdate(updateId)).Returns(DummyUpdate.From("john"));
            var userInfoProviderMock = new Mock<IUserProfileProvider>();

            var userIdentityProvider = IdentityProviderMock.With("john");
            var browser = new Browser(new ActivityFeedApiHandler(userIdentityProvider,
                new CommentsModule(
                    activityFeedServiceMock.Object,
                    userIdentityProvider,
                    userInfoProviderMock.Object)));

            var response = browser.Delete(String.Format("/activityFeed/updates/{0}/comments/4324234", Url.Encode(updateId)));

            Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.NotFound));
        }

        [Test]
        public void Given_authenticated_user_attempt_to_DELETE_comment_by_another_user_returns_Forbidden()
        {
            const string userId = "john";
            const string updateId = "update/111";

            var update = DummyUpdate.From("sam");
            var commentId = update.AddComment("comment", "sam").Id;

            var activityFeedServiceMock = new Mock<IActivityFeedService>(MockBehavior.Strict);
            activityFeedServiceMock.Setup(service => service.GetUpdate(updateId)).Returns(update);

            var userIdentityProvider = IdentityProviderMock.With(userId);
            var browser = new Browser(new ActivityFeedApiHandler(userIdentityProvider,
                new CommentsModule(
                    activityFeedServiceMock.Object,
                    userIdentityProvider,
                    new Mock<IUserProfileProvider>().Object)));

            var response = browser.Delete(string.Format("/activityFeed/updates/{0}/comments/{1}", Url.Encode(updateId), Url.Encode(commentId)));

            Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.Forbidden));
        }
    }
}